
package com.goatstone.server;

import java.util.Collections;
import java.util.Date;
import java.util.TimerTask;
import java.util.Timer;
import java.util.Vector;

//import org.apache.catalina.User;

//import com.apple.mrj.macos.carbon.Timer;
import com.goatstone.client.ServerService;
import com.goatstone.client.ServerResponseData;
import com.goatstone.client.DrawElement;
import com.goatstone.client.ClientRequestData;
import com.goatstone.client.AddElementServerResponseData;
import com.goatstone.client.User;
import com.goatstone.client.ClientRequestDataCheckServer;

//import com.google.gwt.dev.js.rhino.ObjToIntMap.Iterator;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;

//5.19 TODO numbers on events eventNumber=0 ::: Continuations ::: timeout on frontend ::: to much recursion check
//@SuppressWarnings("serial")
public class ServerServiceImpl extends RemoteServiceServlet implements ServerService {
	Vector<DrawElement> masterAlphaDrawElementList = new Vector<DrawElement>(100);
	int masterAlphaDrawElementListMaxSize = 150;
	Vector<User> users = new Vector<User>(100);
	ServerResponseData serverResponseData;
	// thread waits for threadWaitMili (5100) 
	//then a check to see if the timeoutSeconds (5000) has been passed, if yes, continue
//	int timeoutSeconds = 5;
//	int threadWaitMili = 5100;
//	if thread stops waiting it will check this number
	int timeoutSeconds = 49;
	// thread will wait
	int threadWaitMili = 1000*50;
	private static final long serialVersionUID = 1L;
	int maxUsersAccepted = 10;

	public void init(){
		DrawElement alphaDrawElement = new DrawElement();
		alphaDrawElement.unixServerTimeStamp = new Long(0);
		masterAlphaDrawElementList.add(alphaDrawElement);	
		periodicUserListCheck();
	}	
	public void periodicUserListCheck(){
		Timer timer = new Timer();  // create the timer to start the TimerTask thread at the specified time
		TimerTask myTask = new PeriodicUserListCheckTask();   // create the task to perform the work 
		Date now = new Date();
		long period = (1000*60*10);  // every  seconds
		timer.scheduleAtFixedRate(myTask, now, period);
	}
	public class PeriodicUserListCheckTask extends TimerTask{
		//  the amount of time the user is not in contact with the server and the user is deleted from users
		// 1000*60*60 == 1 hour
		Long userTimeOutMili =  new Long(1000*60*20);
		public PeriodicUserListCheckTask( ){
		}	 
		// this method will automatically be invoke when the Timer start this object
		public void run(){
			// iterate through the users array and take out expired elements
			for(int i= 0; i<users.size(); i++){
				User user = users.get(i);
				if(user.latestContactTimeStamp != null){
					if(user.latestContactTimeStamp.compareTo( getTimeStampMilli()-userTimeOutMili )<0){
						users.remove(i);					
						break;
					}	
				}
			}
		}
	}
//	users.remove(users.firstElement());				


	// TODO check to see if the client calling is valid??
	// check to see that none of the users have timed out
	// check to see that the users list is at its maximum
	// if list at maximum  then
	// srd.maxUsersAccepted
	// on timeout 
	// block, admin users
	// to see if a user has expired: update the user.lastContactTimeStamp variable
	public ServerResponseData checkServer(ClientRequestDataCheckServer clientRequestDataCheckServer) {
		
		serverResponseData = new ServerResponseData();

		if ( clientRequestDataCheckServer.userId == null  ){
			// max users reached check assign a client id of 0 and mark them as lurkers 
			if( users.size() >= maxUsersAccepted ){				
				serverResponseData.totalUsers = users.size();
				serverResponseData.message = "lurk";
				serverResponseData.setMode = "lurk";
				serverResponseData.assignedClientId=new Long(0);
				return serverResponseData;							
			}
			// create new user and send to client to init client
			User user = new User(getTimeStampMilli());
//			user.userId = getTimeStampMilli();
			user.latestContactTimeStamp = user.userId;
			users.add(user);
			serverResponseData.totalUsers = users.size();
			serverResponseData.assignedClientId = user.userId;
			serverResponseData.message = "new user made";
			return serverResponseData;				
		}
		
//		else if( isUserValid( clientRequestDataCheckServer.userId )==false ){
		else if( getAlphaDrawUser( clientRequestDataCheckServer.userId )==null && 
				clientRequestDataCheckServer.userId.compareTo( new Long(0) )!=0){
			serverResponseData.totalUsers = users.size();
			serverResponseData.command = "reset";
			serverResponseData.message = "Your session has expired, try reloading the browser";
			return serverResponseData;							
		}
		synchronized ( masterAlphaDrawElementList ) {
			long timeout = System.currentTimeMillis() + (timeoutSeconds*1000) ;
			boolean timeoutComplete = false;
//			serverResponseData.message += "message";
			// init call : is a user calling for the first time? create a user and assign a userId to the user/client
			// check to see if this user is valid userId, a client that has timed out and is now calling again
//			if( isUserValid( clientRequestDataCheckServer.userId ) == false && clientRequestDataCheckServer.userId != 0){
//				serverResponseData.message = "reset";
//				serverResponseData.command = "reset";
//				// the user needs a new id !!!!!!!!!
//				return serverResponseData;
//			}
//			if(clientRequestDataCheckServer.userId != null && isUserValid( clientRequestDataCheckServer.userId ) ){
			if(clientRequestDataCheckServer.userId != null && 
					clientRequestDataCheckServer.userId.compareTo( new Long(0) )!=0 ){
				getAlphaDrawUser(clientRequestDataCheckServer.userId).latestContactTimeStamp = getTimeStampMilli();
			}
			// while the master list last element and latestTimeStampClient is the same wait
			while ( ((Long)((DrawElement)masterAlphaDrawElementList.lastElement()).unixServerTimeStamp).
					compareTo( clientRequestDataCheckServer.latestClientElementTimeStamp ) == 0 && !timeoutComplete ) {
				try {
					if(System.currentTimeMillis() > timeout ){
						timeoutComplete = true;
						masterAlphaDrawElementList.notifyAll();
					}
					masterAlphaDrawElementList.wait(threadWaitMili);
				} catch ( InterruptedException e ) {
					return new ServerResponseData();
				}
			}

			serverResponseData.totalUsers = users.size();
			//if there are elements to send to the client, send them : if ( !timeoutComplete  ) {
			if ( ((Long)((DrawElement)masterAlphaDrawElementList.lastElement()).unixServerTimeStamp).
					compareTo( clientRequestDataCheckServer.latestClientElementTimeStamp ) != 0 ) {
				serverResponseData.alphaDrawMissingElementList = new Vector<DrawElement>();
				for(int i=masterAlphaDrawElementList.size()-1; i>0;  i--){
					if( ((Long)((DrawElement)masterAlphaDrawElementList.get(i)).unixServerTimeStamp).compareTo( 
							clientRequestDataCheckServer.latestClientElementTimeStamp) == 0 ){
						break;						
					}
					serverResponseData.alphaDrawMissingElementList.add( masterAlphaDrawElementList.get(i) ); 
				}
				Collections.reverse(serverResponseData.alphaDrawMissingElementList);
			}
			return serverResponseData;
		}
	}
	// is the user calling in the users list? the user may have expired
	public AddElementServerResponseData addElement( ClientRequestData clientRequestData) {

		if(clientRequestData.userId != null ){
			getAlphaDrawUser(clientRequestData.userId).latestContactTimeStamp = getTimeStampMilli();
		}
		else {			
			AddElementServerResponseData addElementServerResponseData = new AddElementServerResponseData();
			return addElementServerResponseData; 
		} 
		// client is calling without userId, userId has not been set, TODO fix on client, 
		// no interaction until clientId gets set 
		
		DrawElement alphaDrawElement = clientRequestData.drawElement;
		alphaDrawElement.unixServerTimeStamp = getTimeStampMilli() ;
		synchronized ( masterAlphaDrawElementList ) {
			masterAlphaDrawElementList.add( alphaDrawElement );
			
			
			//  if the vector is over masterAlphaDrawElementList then trim it back to maxSIze
			if(masterAlphaDrawElementList.size() >= masterAlphaDrawElementListMaxSize){
				masterAlphaDrawElementList.removeElementAt(0);
			}

			
			masterAlphaDrawElementList.notifyAll();			
			AddElementServerResponseData addElementServerResponseData = new AddElementServerResponseData();
			return addElementServerResponseData; 
		}
	}

	
	public User getAlphaDrawUser(Long userId){
		for(int i= 0; i<users.size(); i++){
			User  alphaDrawUser  = users.get(i);
			if ( alphaDrawUser.userId.compareTo(userId) == 0){
				return alphaDrawUser;
			}
		}
		return null; 
	}

	
	public boolean isUserValid( Long userId ){
		if(userId == null)return false;
		boolean userIsValid = false;
		for(int i= 0; i<users.size(); i++){
			if ( users.get(i).userId.compareTo(userId) == 0){
				userIsValid = true;
			}
		}
		return userIsValid;
	}
	private Long getTimeStampMilli() {
		Date date = new Date();
		return date.getTime();
	}

}